package org.morph.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.net.UnknownHostException;

/**
 * Contains additional IO Functionalities
 * @author Enrico Ludwig
 *
 */
public class ExtendedFile extends File {
	private static final long serialVersionUID = 8769338987723047451L;

	public ExtendedFile(File arg0, String arg1) {
		super(arg0, arg1);
	}

	public ExtendedFile(String arg0, String arg1) {
		super(arg0, arg1);
	}

	public ExtendedFile(String arg0) {
		super(arg0);
	}

	public ExtendedFile(URI arg0) {
		super(arg0);
	}
	
	/**
	 * Fills a file with the given content.
	 * Attention: The file will be OVERWRITTEN!
	 * 
	 * If the file is not existing, it will be created.
	 * If the content is <code>null</code>, a empty file will be created.
	 * 
	 * @param file The file you want to use
	 * @param content The content you want to put in the file
	 * @throws IOException
	 */
	public static void writeFile(File file, String content) throws IOException {
		if (file != null) {
			// Create new file, if the file is not existing, or the file is a
			// directory.
			if (file.isFile() ^ file.createNewFile()) {
				if (content != null) {
					FileWriter     fw = new FileWriter(file);
					BufferedWriter bw = new BufferedWriter(fw);
					
					// Write content or nothing
					bw.write(content != null ? content : "");
					bw.close();
				}
			} else {
				throw new IOException("Could not create File.");
			}
		} else {
			throw new NullPointerException("File could not be null");
		}
	}
	
	/**
	 * Fills a file with the given content.
	 * Attention: The file will be OVERWRITTEN!
	 * 
	 * If the file is not existing, it will be created.
	 * If the content is <code>null</code>, a empty file will be created.
	 * 
	 * @param file The file you want to use
	 * @param content The content you want to put in the file
	 * @throws IOException
	 */
	public static void writeFile(String file, String content) throws IOException {
		writeFile(new File(file), content);
	}
	
	/**
	 * Reads a file completly
	 * @param file File to read
	 * @return The contents from the file
	 * @throws IOException
	 */
	public static String readFile(File file) throws IOException {
		if (file != null) {
			if (file.isFile()) {
				StringBuffer   sb = new StringBuffer();
				FileReader     fr = new FileReader(file);
				BufferedReader br = new BufferedReader(fr);
				
				for (String line = null; (line = br.readLine()) != null; ) {
					sb.append(line);
				}
				
				br.close();
				return sb.toString();
			} else {
				throw new FileNotFoundException("Could not find file to read.");
			}
		} else {
			throw new NullPointerException("File could not be null");
		}
	}

	/**
	 * Reads a file completly
	 * @param file File to read
	 * @return The contents from the file
	 * @throws IOException
	 */
	public static String readFile(String file) throws IOException {
		return readFile(new File(file));
	}
	
	/**
	 * Appends content to a file 
	 * @param file The file you want to add content
	 * @param content The content you want to add to the file
	 * @throws IOException
	 */
	public static void appendFile(File file, String content) throws IOException {
		String oldContent = readFile(file);
		String newContent = oldContent + (content != null ? content : "");
		
		writeFile(file, newContent);
	}
	
	/**
	 * Appends content to a file 
	 * @param file The file you want to add content
	 * @param content The content you want to add to the file
	 * @throws IOException
	 */
	public static void appendFile(String file, String content) throws IOException {
		appendFile(new File(file), content);
	}
	
	/**
	 * Sends a file via TCP to a given host:port
	 * @param file The file you want to send
	 * @param host The destination host
	 * @param port The destination port
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	public static void sendFileTCP(File file, String host, int port) throws UnknownHostException, IOException {
		if (file != null) {
			if (file.isFile()) {
				Socket s = new Socket(host, port);
				if (s.isConnected()) {
					FileInputStream fin = new FileInputStream(file);
					OutputStream os = s.getOutputStream();
					
					// Read / send file
					byte[] buffer = new byte[1024];
					while (fin.read(buffer) > -1) {
						os.write(buffer);
					}
					
					// Close streams
					fin.close();
					os.close();
					s.close();
				} else {
					throw new SocketException("Could not connect to host");
				}
			} else {
				throw new FileNotFoundException("The file " + file + " does not exist.");
			}
		} else {
			throw new NullPointerException("File is null");
		}
	}
	
	/**
	 * Sends a file via TCP to a given host:port
	 * @param file The file you want to send
	 * @param host The destination host
	 * @param port The destination port
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	public static void sendFileTCP(String file, String host, int port) throws UnknownHostException, IOException {
		sendFileTCP(new File(file), host, port);
	}
	
	/**
	 * Creates a temporary Server, listening on given port.
	 * On data incoming, it will saved as file on given location
	 * 
	 * Attention: If the file is already existing, the file will be
	 * overwritten!
	 * @param file
	 * @param port
	 * @throws IOException 
	 */
	public static void receiveFileTCP(File file, int port) throws IOException {
		if (file != null) {
			if (port > -1 && port < 65536) {
				ServerSocket s = new ServerSocket(port);
				Socket c;
				
				if ((c = s.accept()) != null) {
					FileOutputStream fos = new FileOutputStream(file);
					InputStream is = c.getInputStream();
					
					byte[] buffer = new byte[1024];
					while (is.read(buffer) > -1) {
						fos.write(buffer);
					}
					
					is.close();
					fos.close();
					c.close();
				}
			} else {
				throw new IllegalArgumentException("Port need to be a value between 0 and 65535");
			}
		} else {
			throw new NullPointerException("File could not be null");
		}
	}
	
	/**
	 * Creates a temporary Server, listening on given port.
	 * On data incoming, it will saved as file on given location
	 * 
	 * Attention: If the file is already existing, the file will be
	 * overwritten!
	 * @param file
	 * @param port
	 * @throws IOException 
	 */
	public static void receiveFileTCP(String file, int port) throws IOException {
		receiveFileTCP(new File(file), port);
	}
}
